package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"go-web/model"
	"io"
	"log"
	"math/rand"
	"os"
	"time"
)

var db *gorm.DB

func init() {
	conn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s",
		"admin123",
		"Lingmaimai001",
		"mysql8.rwlb.rds.aliyuncs.com:3306",
		"test",
	)

	var err error
	if db, err = gorm.Open("mysql", conn); err != nil {
		log.Fatalf("Fail to connect db : %v", err)
	}

	//defer DB.Close()

	db.LogMode(true)
	//默认加s， grom转义struct名字的时候不用加上s
	//db.SingularTable(true)

	// idle 空闲的
	db.DB().SetMaxIdleConns(40)
	db.DB().SetMaxOpenConns(40)
}

func main() {
	rand.Seed(time.Now().UnixNano())

	gin.SetMode(gin.ReleaseMode)
	f, _ := os.Create("gin.log")
	gin.DefaultWriter = io.MultiWriter(f)

	r := gin.Default()

	r.GET("/get1", func(c *gin.Context) {
		var goods model.Goods
		db.Find( &goods, 1)

		c.JSON(200, gin.H{
			"goods": goods,
		})
	})

	r.GET("/get2", func(c *gin.Context) {
		var goods model.Goods
		db.Find( &goods, 1)

		var order model.Order
		db.Find( &order, 1)

		c.JSON(200, gin.H{
			"goods": goods,
			"order": order,
		})
	})

	r.GET("/getById", func(c *gin.Context) {
		var goods model.Goods
		db.Find( &goods, rand.Intn(300000))

		c.JSON(200, gin.H{
			"goods": goods,
		})
	})

	// innodb行锁测试
	r.GET("/decr", func(c *gin.Context) {
		decr()
		c.JSON(200, gin.H{
			"decr": 0,
		})
	})

	// innodb随机行锁测试
	r.GET("/decrRand", func(c *gin.Context) {
		decrRand()
		c.JSON(200, gin.H{
			"decr": 0,
		})
	})

	// innodb事务+行锁测试
	r.GET("/txAndDecr", func(c *gin.Context) {
		txAndDecr()
		c.JSON(200, gin.H{
			"txAndDecr": 0,
		})
	})

	// innodb事务+随机行锁测试
	r.GET("/txAndRandDecr", func(c *gin.Context) {
		txAndRandDecr()
		c.JSON(200, gin.H{
			"txAndRandDecr": 0,
		})
	})


	r.Run()
}

func decr() {
	var goods model.Goods
	rowsAffected := db.Model(&goods).Where("id = ?", 1).Where("stock - ? >= 0", 1).UpdateColumn("stock", gorm.Expr("stock - ?", 1)).RowsAffected
	if rowsAffected == 0 {
	}
}

func decrRand() {
	id := rand.Intn(300000)
	var goods model.Goods
	rowsAffected := db.Model(&goods).Where("id = ?", id).Where("stock - ? >= 0", 1).UpdateColumn("stock", gorm.Expr("stock - ?", 1)).RowsAffected
	if rowsAffected == 0 {
	}
}

func txAndDecr() {
	tx := db.Begin()
	var goods model.Goods
	rowsAffected := db.Model(&goods).Where("id = ?", 1).Where("stock - ? >= 0", 1).UpdateColumn("stock", gorm.Expr("stock - ?", 1)).RowsAffected
	if rowsAffected == 0 {
		tx.Rollback()
		return
	}

	var order = new(model.Order)
	order.UserId = rand.Intn(10000000)
	order.GoodsId = rand.Intn(10000000)
	order.Number = 1
	if err := tx.Create(order).Error; err != nil {
		tx.Rollback()
		return
	}
	tx.Commit()
}

func txAndRandDecr() {
	userId := rand.Intn(1000000)
	goodsId := rand.Intn(380000)
	tx := db.Begin()
	var goods model.Goods
	rowsAffected := db.Model(&goods).Where("id = ?", goodsId).Where("stock - ? >= 0", 1).UpdateColumn("stock", gorm.Expr("stock - ?", 1)).RowsAffected
	if rowsAffected == 0 {
		tx.Rollback()
		return
	}

	var order = new(model.Order)
	order.UserId = userId
	order.GoodsId = goodsId
	order.Number = 1
	if err := tx.Create(order).Error; err != nil {
		tx.Rollback()
		return
	}
	tx.Commit()
}

